#pragma once
#include "../common/mq_msg/mq_msg.pb.h"
#include "../common/helper.hpp"
#include "../common/log.hpp"
#include "google/protobuf/map.h"
#include <iostream>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>


namespace xzt
{
    // 1.定义队列
    class MsgQueue
    {
    public:
        using ptr = std::shared_ptr<MsgQueue>;
        using args_mapType = google::protobuf::Map<std::string, std::string>;
    public:
        MsgQueue():_name("default"), _exclusive(false), _durable(false), 
                _auto_delete(false), _args() {}

        MsgQueue(const std::string& name, bool exclusive, bool durable, bool auto_delete, const args_mapType& args)
        : _name(name), _exclusive(exclusive), _durable(durable), _auto_delete(auto_delete), _args(args) {}

        // args存储键值对，在数据库中时，会组织格式化字符串存储：key1=val1&key2=val2...
        void setArgs(const std::string& s_args)
        {
            std::vector<std::string> vs;    // 用于存储分割后的kv键值对
            StringHelper::split(s_args, "&", vs);
            
            for(auto& kv: vs)
            {
                size_t pos = kv.find("=");
                std::string key = kv.substr(0, pos);
                std::string val = kv.substr(pos + 1);
                _args.insert({key, val});
            }
        }

        std::string getArgs()
        {
            std::string result = "";
            for(auto& kv: _args)
            {
                result += kv.first + "=" + kv.second + "&";
            }
            if(result.empty() == false)
            {
                result.pop_back();
            }
            return result;
        }
    public:
        std::string _name;   // 队列名称
        bool _exclusive;  // 是否独占标志
        bool _durable;       // 是否持久化标志
        bool _auto_delete;   // 是否自动删除标志
        google::protobuf::Map<std::string, std::string> _args;  // 其他参数
    };

    // 2.定义队列数据持久化管理类 -- 数据存储在sqlite数据库中
    using QueueMap = std::unordered_map<std::string, MsgQueue::ptr>;
    class MsgQueueMapper        // 充当中间层
    {
    public:
        MsgQueueMapper(const std::string& dbfile): _sql_helper(dbfile) 
        {
            // 若文件所在目录不存在，需要先创建父级目录
            std::string path = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(path);
            // sql声明文件（不存在则创建）
            _sql_helper.open();
            createTable();
        }

        bool createTable()
        {
            std::stringstream ss;
            ss << "create table if not exists queue_table (\
            name varchar(32) primary key, \
            exclusive int, \
            durable int, \
            auto_delete int, \
            args varchar(128));";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if(ret == false)
            {
                ELOG("MsgQueueMapper::createTable() -> 数据库表创建失败");
                abort();
            }
            return ret;
        }

        void removeTable()
        {
            std::stringstream ss;
            ss << "drop table if exists queue_table;";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if(ret == false)
            {
                ELOG("MsgQueueMapper::removeTable() -> 数据库表删除失败");
                abort();
            }
        }

        bool insert(MsgQueue::ptr& p_msgqueue)
        {
            std::stringstream ss;
            ss << "insert into queue_table values(";

            ss << "'" << p_msgqueue->_name << "', ";
            ss << p_msgqueue->_exclusive << ", ";
            ss << p_msgqueue->_durable << ", ";
            ss << p_msgqueue->_auto_delete << ", ";
            ss << "'" << p_msgqueue->getArgs() << "');";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if(ret == false)
            {
                ELOG("MsgQueueMapper::insert() -> 数据库表内数据插入失败");
                abort();
            }
            return ret;
        }

        void remove(const std::string& name)
        {
            std::stringstream ss;
            ss << "delete from queue_table where name='" << name << "';";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if(ret == false)
            {
                ELOG("MsgQueueMapper::remove() -> 数据库表内数据插入失败");
                abort();
            }
        }

        QueueMap recovery()
        {
            QueueMap result{};
            std::string sql = "select name, exclusive, durable, auto_delete, args from queue_table;";
            bool ret = _sql_helper.exec(sql, _selectCallback, &result);
            return result;
        }
    private:
        static int _selectCallback(void* arg, int col_num, char** row, char** fields)
        {
            auto p_map = static_cast<QueueMap*>(arg);

            auto p_msgqueue = std::make_shared<MsgQueue>();
            p_msgqueue->_name = row[0];
            p_msgqueue->_exclusive = static_cast<bool>(row[1]);
            p_msgqueue->_durable = static_cast<bool>(row[2]);
            p_msgqueue->_auto_delete = static_cast<bool>(row[3]);
            if(row[4])
            {
                p_msgqueue->setArgs(row[4]);
            }

            p_map->insert({p_msgqueue->_name, p_msgqueue});
            return 0;   // 表示回调函数成功返回
        }
    private:
        SqliteHelper _sql_helper;
    };

    // 3.定义队列内存数据管理类
    class MsgQueueManager       // 最外层，提供对外接口
    {
    public:
        using ptr = std::shared_ptr<MsgQueueManager>;
    public:
        MsgQueueManager(const std::string& dbfile): _mapper(dbfile)
        {
            _msg_queues = _mapper.recovery();
        }

        // 声明队列
        bool declareMsgQueue(const std::string& name, bool exclusive, 
                            bool durable, bool auto_delete, 
                            const MsgQueue::args_mapType& args)
        {
            bool ret = true;
            // 注意判断队列是否存在和插入的操作期间，必须持续持有锁，否则有线程安全的问题
            std::lock_guard<std::mutex> lk(_mutex);
            // 判断该队列是否已经存在，若存在则无需重复声明
            if(_msg_queues.find(name) != _msg_queues.end())
            {
                return ret;
            }

            auto p_MsgQueue = std::make_shared<MsgQueue>(name, exclusive, durable, auto_delete, args);
            
            // 判断是否需要持久化存储
            if(durable == true)
            {
                ret = _mapper.insert(p_MsgQueue);
            }
            _msg_queues.insert({name, p_MsgQueue});
            return ret;
        }

        // 删除队列
        void deleteMsgQueue(const std::string& name)
        {
            std::lock_guard<std::mutex> lk(_mutex);
            // 判断该队列是否存在，若不存在则无需调用删除接口
            auto iter = _msg_queues.find(name);
            if(iter == _msg_queues.end())
            {
                return;
            }
            // 若非持久存储，则没有进入数据库删除的必要
            if(iter->second->_durable == true)
            {
                _mapper.remove(name);
            }
            _msg_queues.erase(name);
        }

        // 获取队列
        MsgQueue::ptr selectMsgQueue(const std::string& name)
        {
            std::lock_guard<std::mutex> lk(_mutex);
            auto iter = _msg_queues.find(name);
            if(iter == _msg_queues.end())    // 未找到
            {
                return MsgQueue::ptr();
            }
            return iter->second;
        }

        // 获取内存中的所有队列
        QueueMap allQueues()
        {
            std::lock_guard<std::mutex> lk(_mutex);
            return _msg_queues;
        }

        size_t size()
        {
            std::lock_guard<std::mutex> lk(_mutex);
            return _msg_queues.size();
        }

        // 判断队列是否存在
        bool exists(const std::string& name) 
        {
            std::lock_guard<std::mutex> lk(_mutex);
            auto iter = _msg_queues.find(name);
            if(iter == _msg_queues.end())    // 未找到
            {
                return false;
            }
            return true;
        }

        // 清理所有队列数据
        void clear()
        {
            std::lock_guard<std::mutex> lk(_mutex);
            _mapper.removeTable();
            _msg_queues.clear();
        }

    private:
        std::mutex _mutex;
        MsgQueueMapper _mapper;
        QueueMap _msg_queues;
    };

}